/*
 * Connection.cpp
 * This class provides methods to establish connection, sending IMessage and receiving IMessage
 *
 */

#include "Connection.hpp"

/**
 * Constructor- initiate Class fields and socket connection
 */
Connection::Connection ( int port , string host )
{
	logger =  new Logger("CommunicationException.log");
	connectionPort = port ;
	connectionHost = host ;
	try
	{
		context = new context_t ( 1 ) ;
		socket = new socket_t ( * context , ZMQ_REQ ) ;
		//connect socket to host
		char buffer [ 50 ] ;
		sprintf ( buffer , "tcp://%s:%d" , connectionHost.c_str () , connectionPort ) ;
		( * socket ).connect ( buffer ) ;
	}
	catch (zmq::error_t& e)
	{
		logZMQException("[Connection::constructor] : error creating connection.", e );
	}

}

Connection::Connection ( int port  )
{
	logger = new Logger("CommunicationException.log");
	connectionPort = port ;
	try
	{
		context = new context_t ( 1 ) ;
		socket = new socket_t ( * context , ZMQ_REP ) ;
		string bindstr = string("tcp://*:") + int2str(connectionPort);
		(* socket).bind(bindstr.c_str());
	}
	catch (zmq::error_t& e)
	{
		logZMQException("[Connection::constructor] : error creating connection.", e );
	}
}


Connection::~Connection ()
{
	( * socket ).close () ;
	delete(context);
	delete(socket);
	delete(logger);

}

/**
 * public
 * Sends message to server and wait for reply
 */
bool Connection::sendAndWaitForReceive ( Message request , Message * received)
{
	bool success = send(request);
	if (success)
		return receive (received);
	return success;
}

bool Connection::sendAndWaitForReceiveWithTimeout ( Message request ,Message * received ,  int timeoutsec )
{
	setSocketTimeout(ZMQ_SNDTIMEO, timeoutsec);
	setSocketTimeout(ZMQ_RCVTIMEO, timeoutsec);
	bool success = sendAndWaitForReceive ( request , received);
	setSocketTimeout(ZMQ_SNDTIMEO, -1);
	setSocketTimeout(ZMQ_RCVTIMEO, -1);
	return success;
}

bool Connection::sendWithTimeout(Message message ,  int timeoutsec )
{
	setSocketTimeout(ZMQ_SNDTIMEO, timeoutsec);
	bool success = send(message);
	setSocketTimeout(ZMQ_SNDTIMEO, -1);
	return success;
}

bool Connection::receiveWithTimeout (Message * received , int timeoutsec )
{
	setSocketTimeout(ZMQ_RCVTIMEO, timeoutsec);
	bool success =  receive (received);
	setSocketTimeout(ZMQ_RCVTIMEO, -1);
	return success;
}


bool Connection::receive (Message * received)
{
	//create reply obj and receiving it
	message_t msg ;
	bool success;

	try
	{
		success = ( * socket ).recv ( & msg ) ;
	}
	catch (zmq::error_t& e)
	{
		logZMQException("[Connection::receive] : exception receiving message.", e );
	}

	if (success)
	{
		//fill received message
		received->ParseFromArray(msg.data(), msg.size());
	}
	//return success
	return success ;
}

bool Connection::send(Message message)
{
	message_t * msg = createMessage_t(message);
	bool success = send ( msg  ) ;
	delete (msg);
	return success;
}



message_t * Connection::createMessage_t(Message message)
{
	string sirRequest = message.SerializeAsString () ;
	message_t * requestmsg;
	try
	{
		requestmsg = new message_t ( sirRequest.size () + 1 ) ;
		memcpy ( ( void * ) ( * requestmsg ).data () , ( void * ) sirRequest.c_str () , sirRequest.size () + 1 ) ;
	}
	catch (zmq::error_t& e)
	{
		logZMQException("[Connection::createMessage_t] : error initiating message_t object.", e );
	}

	return requestmsg;
}

bool Connection::send ( message_t *request )
{
	//send the message
	bool success;
	try
	{
		success = ( * socket ).send ( * request ) ;
	}
	catch (zmq::error_t& e)
	{
		logZMQException("[Connection::send] : exception sending message.", e );
	}
	delete ( request ) ;
	return success;
}

void Connection::setSocketTimeout(int opt, int timeoutsec)
{
	int timeoutmilis;
	if (timeoutsec != -1)
		 timeoutmilis = timeoutsec * 1000;
	try
	{
		(*socket).setsockopt(opt,&timeoutmilis,sizeof(timeoutmilis));
	}
	catch (zmq::error_t& e)
	{
		logZMQException("[Connection::setSocketTimeout] : exception setting socket timeout.", e );
	}
}


void Connection::logZMQException(string text, zmq::error_t& e )
{
	text.append(" (exception info: ");
	text.append(int2str(e.num()));
	text.append("- ");
	text.append(e.what());
	text.append(")");
	logger->logError(text);
}

